package com.gitee.cirnochat.user.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.cirnochat.common.event.UserEvent;
import com.gitee.cirnochat.common.event.UserEventFactory;
import com.gitee.cirnochat.common.exception.BusinessException;
import com.gitee.cirnochat.common.model.enums.ErrorCode;
import com.gitee.cirnochat.common.model.vo.resp.PageVo;
import com.gitee.cirnochat.common.utils.JwtTool;
import com.gitee.cirnochat.user.mapper.UserMapper;
import com.gitee.cirnochat.user.model.entity.*;
import com.gitee.cirnochat.user.model.enums.UserStatus;
import com.gitee.cirnochat.user.model.vo.req.user.UserEmailResetPasswordRequest;
import com.gitee.cirnochat.user.model.vo.req.user.UserEmailSetPasswordRequest;
import com.gitee.cirnochat.user.model.vo.req.user.UserInfoUpdateRequest;
import com.gitee.cirnochat.user.model.vo.req.user.UserOnlineStatusUpdateRequest;
import com.gitee.cirnochat.user.model.vo.resp.UserVo;
import com.gitee.cirnochat.user.service.UserChatSettingService;
import com.gitee.cirnochat.user.service.UserPrivacySettingService;
import com.gitee.cirnochat.user.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

import static com.gitee.cirnochat.common.constant.RedisKey.*;

/**
 * @author FlanChan
 * @description 针对表【user(用户信息)】的数据库操作Service实现
 * @createDate 2025-04-17 14:31:33
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserChatSettingService chatSettingService;
    private final UserPrivacySettingService userPrivacySettingService;
    private final StringRedisTemplate redisTemplate;
    private final JwtTool jwtTool;
    private final PasswordEncoder passwordEncoder;
    private final ApplicationEventPublisher eventPublisher;

    @Override
    public void initUserInfo(Long userId) {
        UserChatSetting userChatSetting = new UserChatSetting();
        userChatSetting.setUserId(userId);
        chatSettingService.save(userChatSetting);
        UserPrivacySetting userPrivacySetting = new UserPrivacySetting();
        userPrivacySetting.setUserId(userId);
        userPrivacySettingService.save(userPrivacySetting);
    }

    //endregion

    //region 查询用户信息相关

    @Override
    public UserPermission getUserPermissionById(Long userId, Long currentUserId) {
        return this.baseMapper.selectUserPermissionById(userId, currentUserId);
    }

    @Override
    public Long validateAndParseUserId(String token,String deviceId) {
        Long userId = jwtTool.parseTokenToUserId(token);
        return Boolean.TRUE.equals(redisTemplate.hasKey(ACCESS_TOKEN_KEY + String.format("%s#%s", deviceId, userId))) ? userId : null;
    }

    @Override
    public UserVo getUserInfoWithFriendRelById(Long friendId, Long userId) {
        UserDetail userDetail = this.baseMapper.selectUserInfoWithFriendRelById(friendId, userId);
        return userDetail != null ? userDetail.toUserVo() : null;
    }

    @Override
    public UserVo getUserInfoById(Long userId) {
        return new UserVo(this.getById(userId));
    }

    @Override
    public UserVo searchByUUID(Long userId, String uuid) {
        UserDetail detail = this.baseMapper.searchByUUID(uuid,  userId);
        return detail == null ? null : new UserVo(detail);
    }

    @Override
    public PageVo<UserVo> searchByKeyword(Long userId, String keyword, int page, int size, int sort) {
        int offset = (page - 1) * size;
        List<UserVo> users = this.baseMapper.searchByKeyword(offset, size, keyword, sort, userId).stream()
                .map(UserDetail::toUserVo).toList();
        long totalCount = this.count(new LambdaQueryWrapper<>(User.class)
                .like(User::getNickname, keyword)
                .or()
                .like(User::getEmail, keyword));
        int pages = (int) Math.ceil((double) totalCount / size);
        return new PageVo<>(page, size, pages, (int) totalCount, users);
    }

    @Override
    public boolean existsById(Long userId) {
        return this.getOne(new LambdaQueryWrapper<>(User.class)
                .eq(User::getId, userId)) != null;
    }

    //endregion

    //region 更新用户信息相关

    @Override
    public void updateUserAvatar(Long userId, String avatarUri) {
        this.update(new LambdaUpdateWrapper<>(User.class)
                .eq(User::getId, userId)
                .set(User::getAvatar, avatarUri));
    }

    @Override
    public void updateUserInfo(UserInfoUpdateRequest req) {
        Long userId = req.getUserId();
        String uuid = req.getUuid();
        String nickname = req.getNickname();
        Integer gender = req.getGender();
        String region = req.getRegion();
        String mobile = req.getMobile();
        LocalDate birthday = req.getBirthday();
        String signature = req.getSignature();

        if (uuid != null) {
            final User originalUser = this.getById(userId);

            if (uuid.equals(originalUser.getUuid())) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "新凭证必须与原凭证不同");
            }

            final User u = this.getOne(new LambdaQueryWrapper<>(User.class)
                    .eq(User::getUuid, uuid));

            if (u != null) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户唯一凭证已存在");
            }
        }

        this.update(new LambdaUpdateWrapper<>(User.class)
                .eq(User::getId, userId)
                .set(uuid != null, User::getUuid, uuid)
                .set(nickname != null, User::getNickname, nickname)
                .set(gender != null, User::getGender, gender)
                .set(region != null, User::getRegion, region)
                .set(mobile != null, User::getMobile, mobile)
                .set(birthday != null, User::getBirthday, birthday)
                .set(signature != null, User::getSignature, signature)

        );
    }

    @Override
    public void updateUserPreferStatus(UserOnlineStatusUpdateRequest req, Long userId) {
        UserStatus status = req.getStatus();
        if (status == UserStatus.OFFLINE) {
            return;
        }
        this.update(new LambdaUpdateWrapper<>(User.class)
                .eq(User::getId, userId).set(User::getPreferStatus, status));
        redisTemplate.opsForValue().set(USER_STATE_KEY + userId, status.toString());
        // 如果用户在线状态为离线 | 隐身，则需要发布离线通知

        UserEvent userEvent = UserEventFactory.createUserEvent(this, userId, status, null, null, null);
        eventPublisher.publishEvent(userEvent);
    }

    @Override
    public void updateUserPassword(UserEmailResetPasswordRequest req) {
        final User user = this.getOne(new LambdaUpdateWrapper<>(User.class)
                .eq(User::getEmail, req.getEmail()));
        if (user == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户不存在");
        }
        if (user.getPassword() == null || user.getPassword().isEmpty()) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户未设置密码");
        }
        String oldPassword = req.getOldPassword();
        String newPassword = req.getNewPassword();
        if (oldPassword.equals(newPassword)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "新密码不能和旧密码相同");
        }
        if (!req.getEmail().equals(user.getEmail())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "邮箱不匹配");
        }
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "旧密码错误");
        }
        String code = redisTemplate.opsForValue().get(EMAIL_RESET_PASS_CAPTCHA_CACHE_KEY + req.getEmail());
        if (code == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码已失效");
        }
        if (!code.equals(req.getCode())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码错误");
        }
        user.setPassword(passwordEncoder.encode(req.getNewPassword()));
        this.updateById(user);
        // 清除缓存中的验证码
        redisTemplate.delete(EMAIL_RESET_PASS_CAPTCHA_CACHE_KEY + req.getEmail());
    }

    @Override
    public void updateUserPassword(UserEmailSetPasswordRequest req, Long userId) {
        String password = req.getPassword();
        String confirmPassword = req.getConfirmPassword();

        if (!password.equals(confirmPassword)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "两次密码不一致");
        }

        User user = this.getById(userId);
        if (!req.getEmail().equals(user.getEmail())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "邮箱不匹配");
        }
        if (user.getPassword() != null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户已设置密码");
        }
        String code = redisTemplate.opsForValue().get(EMAIL_RESET_PASS_CAPTCHA_CACHE_KEY + req.getEmail());
        if (code == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码已失效");
        }
        if (!code.equals(req.getCode())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码错误");
        }
        // 更新用户的密码
        user.setPassword(passwordEncoder.encode(password));
        this.updateById(user);
        // 清除缓存中的验证码
        redisTemplate.delete(EMAIL_RESET_PASS_CAPTCHA_CACHE_KEY + req.getEmail());
    }

    // endregion
}




